package jblip.gui.data;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import jblip.resources.User;

public class Cache {
  private final long item_ttl;

  private final Map<String, CacheItem<User>> users;

  public class CacheItem<T extends Serializable> implements Serializable {
    private static final long serialVersionUID = 1L;

    private final T item;

    private final long timeout;

    public CacheItem(T object) {
      this(object, item_ttl);
    }

    public CacheItem(T object, long ttl) {
      if (object == null) {
        throw new NullPointerException("Null object given to the cache");
      }

      if (ttl < 0) {
        throw new IllegalArgumentException("Cache time to live is negative");
      }

      this.item = object;
      this.timeout = System.currentTimeMillis() + ttl;
    }

    public T getItem() {
      return this.item;
    }

    public boolean expired() {
      return (System.currentTimeMillis() > timeout);
    }
  }  

  public Cache(File cache_file) throws IOException,
      ClassNotFoundException {
    if (!cache_file.setReadOnly()) {
      throw new IOException("Can't lock cache file");
    }

    item_ttl = 1000 * 900; // 15 min
    users = new HashMap<String, CacheItem<User>>();
    
    if (cache_file.exists()) {
      loadCacheItems(cache_file);
    }

    cache_file.setWritable(true);
  }

  @SuppressWarnings("unchecked")
  private void loadCacheItems(File cache_file) throws IOException,
      ClassNotFoundException {
    final ObjectInputStream input =
        new ObjectInputStream(new FileInputStream(cache_file));

    while (input.available() > 0) {
      final CacheItem<?> citem = (CacheItem<?>) input.readObject();
      if (citem.getItem() instanceof User) {
        final CacheItem<User> user_cache = (CacheItem<User>) citem;
        users.put(user_cache.getItem().getLogin(), user_cache);
      }
    }

    input.close();
  }

  public synchronized User getUser(final String id) {
    if (users.containsKey(id)) {
      return users.get(id).item;
    } else {
      return null;
    }
  }

  public synchronized void setUser(final User user) {
    users.put(user.getLogin(), new CacheItem<User>(user));
  }

  public synchronized Set<String> getExpiredUserIDs() {
    Set<String> expired = new HashSet<String>();
    for (CacheItem<User> user_item : users.values()) {
      if (user_item.expired()) {
        expired.add(user_item.item.getLogin());
      }
    }

    return expired;
  }
}
